/*
 * Copyright (c) 2011, Andrew Cole
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package net.andycole.xmiparser.model;

import java.util.Iterator;

import javax.jmi.xmi.XmiReader;
import javax.jmi.model.ModelPackage;
import javax.jmi.model.MofPackage;
import javax.jmi.reflect.RefPackage;
import org.netbeans.api.mdr.MDRepository;
import org.openide.util.Lookup;

/**
 * Creates meta-models from MOF models from a given MDR repository 
 * (and returns the newly-created extent).
 * 
 * @author Andrew Cole
 */
public class RepositoryManager {

   private String metamodelName;
   private boolean createNewExtent;
   private boolean createNewMetamodel;
   private String topPkgName;
   private String xmiSourceFile;

   private MDRepository repository;

   /**
    * Constructor. Sets the repository to be operated on.
    * @param repository
    */
   public RepositoryManager(MDRepository repository)
   {
      createNewExtent = true;
      createNewMetamodel = false;
      this.repository = repository;
   }

   public void setMMDetails(String metamodelName, String xmiSourceFile,
                            String topPkgName)
   {
      this.metamodelName = metamodelName;
      this.xmiSourceFile = xmiSourceFile;
      this.topPkgName = topPkgName;
   }

   public RefPackage createDatamodel(String extentName) throws java.lang.Exception
   {
//      boolean fail = true;
      RefPackage uml = null;
//      repository.beginTrans(true);
//      try {
         // create a new MOF extent
         uml = repository.getExtent(extentName);
         if (uml != null && createNewExtent)
            uml.refDelete();

         uml = repository.createExtent(extentName, getUmlPackage());

//         fail = false;
//      } catch (Exception e) {
//         System.err.println("Fatal error reading XMI for UML extent.");
//         e.printStackTrace(System.err);
//      } finally {
         // commit/rollback the transaction
//         repository.endTrans(fail);
//      }

      return uml;
   }

   private MofPackage getUmlPackage() throws Exception
   {
      // get the MOF extent containing definition of UML metamodel
      XmiReader reader = (XmiReader) Lookup.getDefault().lookup(XmiReader.class);
      ModelPackage umlMM = (ModelPackage) repository.getExtent(metamodelName);
      if (createNewMetamodel)
      {
         umlMM.refDelete();
         umlMM = null;
      }

      if (umlMM == null) {
         // it is not present -> create it
         umlMM = (ModelPackage) repository.createExtent(metamodelName);
      }

      // find package named "UML" in this extent
      MofPackage result = getUmlPackage(umlMM);
      if (result == null) {
         // it cannot be found -> netbeans UML metamodel is not loaded -> load it from XMI
         String resStr = this.getClass().getResource(xmiSourceFile).toString();

         reader.read(resStr, umlMM);
         // try to find the "UML" package again
         result = getUmlPackage(umlMM);
      }
      return result;
   }

   /** Finds "UML" package in a given extent
    * @param umlMM MOF extent that should be searched for "UML" package.
    */
   private MofPackage getUmlPackage(ModelPackage umlMM)
   {
      // iterate through all instances of package
      for (Iterator it = umlMM.getMofPackage().refAllOfClass().iterator(); it.hasNext();) {
         MofPackage pkg = (MofPackage) it.next();
         // is the package topmost and is it named "UML"?
         if (pkg.getContainer() == null && topPkgName.equals(pkg.getName())) {
             // yes -> return it
             return pkg;
         }
      }
      // a topmost package named "UML" could not be found
      return null;
   }

}
